package 极客时间.concurrence.并发编程实战篇.读写锁.多人操作修改订单数据;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.*;

/**
 * @Author idea
 * @Date created in 4:01 下午 2020/8/1
 */
public class OrderDao {

    //volatile对于map不是太凑效果
    static volatile Map<Integer, Order> ORDER_MAP = new ConcurrentHashMap<>(4);

    private final static ReentrantLock reentrantLock = new ReentrantLock();

    private final static ReadWriteLock READ_WRITE_LOCK = new ReentrantReadWriteLock();

    private final static Lock READ_LOCK = READ_WRITE_LOCK.readLock();

    private final static Lock WRITE_LOCK = READ_WRITE_LOCK.writeLock();

    private final static StampedLock STAMPED_LOCK = new StampedLock();


    static {
        ORDER_MAP.put(1, new Order(1, "2020-03-14-xxopaiqw", 1));
//        ORDER_MAP.put(2, new Order(2, "2020-03-14-xxopaiqw", 1));
//        ORDER_MAP.put(3, new Order(3, "2020-03-14-xxopaiqw", 1));
//        ORDER_MAP.put(4, new Order(4, "2020-03-14-xxopaiqw", 1));
    }

    public Order queryOrder(Integer id) {
        return ORDER_MAP.get(id);
    }

    /**
     * 不加锁处理，会有线程安全问题
     * <p>
     * 2000线程并发，130毫秒
     *
     * @param id
     * @return
     */
    public Boolean updateOrderV1(Integer id) {
        Order order = queryOrder(id);
        int currentVersion = order.getVersion();
        order.setVersion(currentVersion + 1);
        ORDER_MAP.put(id, order);
        return true;
    }


    /**
     * 加锁处理
     * <p>
     * 2000线程并发 202毫秒
     *
     * @param id
     * @return
     */
    public Boolean updateOrderV2(Integer id) {
        synchronized (this) {
            Order order = queryOrder(id);
            int currentVersion = order.getVersion();
            order.setVersion(currentVersion + 1);
            ORDER_MAP.put(id, order);
            return true;
        }
    }


    /**
     * 轻量级锁
     * 2000线程 144毫秒
     *
     * @param id
     * @return
     */
    public Boolean updateOrderV3(Integer id) {
        reentrantLock.lock();
        try {
            Order order = queryOrder(1);
            int currentVersion = order.getVersion();
            order.setVersion(currentVersion + 1);
            ORDER_MAP.put(id, order);
            return true;
        } finally {
            reentrantLock.unlock();
        }
    }

    /**
     * 轻量级锁 读写锁
     * 2000线程 164毫秒
     *
     * @param id
     * @return
     */
    public Boolean updateOrderV4(int id) {
        READ_LOCK.lock();
        Order temp;
        try {
            temp = queryOrder(id);
        } finally {
            READ_LOCK.unlock();
        }
        WRITE_LOCK.lock();
        try {
            temp.setVersion(temp.getVersion() + 1);
            ORDER_MAP.put(id, temp);
            return true;
        } finally {
            WRITE_LOCK.unlock();
        }

    }

    /**
     * 采用更加细粒度的锁来处理 STAMPED_LOCK 不支持锁的降级操作，这里要注意 每次加锁之后需要做释放
     *
     * @param id
     * @return
     */
    public Boolean updateOrderV5(int id) {
        Order temp;
        long stamp = STAMPED_LOCK.tryOptimisticRead();
        try {
            if (!STAMPED_LOCK.validate(stamp)) {
                stamp = STAMPED_LOCK.readLock();
                temp = queryOrder(id);
            } else {
                temp = queryOrder(id);
            }
        } finally {
            STAMPED_LOCK.unlockRead(stamp);
        }
        stamp = STAMPED_LOCK.writeLock();
        try {
            temp.setVersion(temp.getVersion() + 1);
            ORDER_MAP.put(id, temp);
        } finally {
            STAMPED_LOCK.unlockWrite(stamp);
        }
        return true;
    }

    public void display() {
        System.out.println(ORDER_MAP);
    }

    public static void main(String[] args) throws InterruptedException {
        OrderDao od = new OrderDao();
        CountDownLatch countDownLatch = new CountDownLatch(2000);
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 2000; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    od.updateOrderV5(1);
                    countDownLatch.countDown();
                }
            });
            thread.start();
        }
        countDownLatch.await();
        long end = System.currentTimeMillis();
        od.display();
        System.out.println("消耗时间：" + (end - begin));
    }
}
